﻿using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using MassPokerTool.Infrastructure.Extensions;
using MassPokerTool.Infrastructure.Hooking.Data;
using MassPokerTool.Infrastructure.Ocr.Chars;

namespace MassPokerTool.Infrastructure.Ocr
{
    public class ScreenCaptureChatRecognizer : ITextRecognizer<ScreenCaptureBitBltHookData>
    {
        private const int YOffset = 2;
        private const int LineHeight = 13;
        private readonly IList<CharPattern> _charPatterns;

        public ScreenCaptureChatRecognizer()
        {
            _charPatterns = new List<CharPattern>
                {
                    new SpecialSymbolCharPattern("#", (Bitmap) Image.FromFile("font/#.bmp", false)),
                    new SpecialSymbolCharPattern("$", (Bitmap) Image.FromFile("font/$.bmp", false)),
                    new SpecialSymbolCharPattern(".", (Bitmap) Image.FromFile("font/dot.bmp", false)),
                    new SpecialSymbolCharPattern(",", (Bitmap) Image.FromFile("font/comma.bmp", false)),
                    new SpecialSymbolCharPattern("'", (Bitmap) Image.FromFile("font/apostroph.bmp", false)),
                    new SpecialSymbolCharPattern(":", (Bitmap) Image.FromFile("font/colon.bmp", false)),
                    new SpecialSymbolCharPattern("(", (Bitmap) Image.FromFile("font/lbracket.bmp", false)),
                    new SpecialSymbolCharPattern(")", (Bitmap) Image.FromFile("font/rbracket.bmp", false)),
                    new NumberCharPattern("0", (Bitmap) Image.FromFile("font/0.bmp", false)),
                    new NumberCharPattern("1", (Bitmap) Image.FromFile("font/1.bmp", false)),
                    new NumberCharPattern("2", (Bitmap) Image.FromFile("font/2.bmp", false)),
                    new NumberCharPattern("3", (Bitmap) Image.FromFile("font/3.bmp", false)),
                    new NumberCharPattern("4", (Bitmap) Image.FromFile("font/4.bmp", false)),
                    new NumberCharPattern("5", (Bitmap) Image.FromFile("font/5.bmp", false)),
                    new NumberCharPattern("6", (Bitmap) Image.FromFile("font/6.bmp", false)),
                    new NumberCharPattern("7", (Bitmap) Image.FromFile("font/7.bmp", false)),
                    new NumberCharPattern("8", (Bitmap) Image.FromFile("font/8.bmp", false)),
                    new NumberCharPattern("9", (Bitmap) Image.FromFile("font/9.bmp", false)),
                    new CharPattern("a", (Bitmap) Image.FromFile("font/a.bmp", false)),
                    new CapitalCharPattern("A", (Bitmap) Image.FromFile("font/a_.bmp", false)),
                    new CharPattern("b", (Bitmap) Image.FromFile("font/b.bmp", false)),
                    new CapitalCharPattern("B", (Bitmap) Image.FromFile("font/b_.bmp", false)),
                    new CharPattern("c", (Bitmap) Image.FromFile("font/c.bmp", false)),
                    new CharPattern("d", (Bitmap) Image.FromFile("font/d.bmp", false)),
                    new CapitalCharPattern("D", (Bitmap) Image.FromFile("font/d_.bmp", false)),
                    new CharPattern("e", (Bitmap) Image.FromFile("font/e.bmp", false)),
                    new CapitalCharPattern("E", (Bitmap) Image.FromFile("font/e_.bmp", false)),
                    new CharPattern("f", (Bitmap) Image.FromFile("font/f.bmp", false)),
                    new CharPattern("g", (Bitmap) Image.FromFile("font/g.bmp", false)),
                    new CapitalCharPattern("G", (Bitmap) Image.FromFile("font/g_.bmp", false)),
                    new CharPattern("h", (Bitmap) Image.FromFile("font/h.bmp", false)),
                    new CapitalCharPattern("H", (Bitmap) Image.FromFile("font/h_.bmp", false)),
                    new CharPattern("i", (Bitmap) Image.FromFile("font/i.bmp", false)),
                    new CapitalCharPattern("K", (Bitmap) Image.FromFile("font/k_.bmp", false)),
                    new CharPattern("l", (Bitmap) Image.FromFile("font/l.bmp", false)),
                    new CharPattern("m", (Bitmap) Image.FromFile("font/m.bmp", false)),
                    new CapitalCharPattern("M", (Bitmap) Image.FromFile("font/m_.bmp", false)),
                    new CharPattern("n", (Bitmap) Image.FromFile("font/n.bmp", false)),
                    new CapitalCharPattern("N", (Bitmap) Image.FromFile("font/n_.bmp", false)),
                    new CharPattern("o", (Bitmap) Image.FromFile("font/o.bmp", false)),
                    new CharPattern("p", (Bitmap) Image.FromFile("font/p.bmp", false)),
                    new CharPattern("r", (Bitmap) Image.FromFile("font/r.bmp", false)),
                    new CapitalCharPattern("R", (Bitmap) Image.FromFile("font/r_.bmp", false)),
                    new CharPattern("s", (Bitmap) Image.FromFile("font/s.bmp", false)),
                    new CharPattern("t", (Bitmap) Image.FromFile("font/t.bmp", false)),
                    new CapitalCharPattern("T", (Bitmap) Image.FromFile("font/t_.bmp", false)),
                    new CharPattern("u", (Bitmap) Image.FromFile("font/u.bmp", false)),
                    new CharPattern("v", (Bitmap) Image.FromFile("font/v.bmp", false)),
                    new CharPattern("w", (Bitmap) Image.FromFile("font/w.bmp", false)),
                    new CapitalCharPattern("W", (Bitmap) Image.FromFile("font/w_.bmp", false)),
                    new CharPattern("x", (Bitmap) Image.FromFile("font/x.bmp", false)),
                    new CharPattern("y", (Bitmap) Image.FromFile("font/y.bmp", false)),
                    new CapitalCharPattern("Y", (Bitmap) Image.FromFile("font/y_.bmp", false)),
                };
        }

        public string RecognizeText(ScreenCaptureBitBltHookData source)
        {
            if (source.SerializedBmp == null)
                return "";
            using (var ms = new MemoryStream(source.SerializedBmp))
            {
                var bitmap = new Bitmap(ms);
                var recognizedText = new StringBuilder();
                bitmap.BlackAndWhite(true);
                for (var i = YOffset; i < bitmap.Height; i += LineHeight)
                {
                    var lineHeight = bitmap.Height - i < LineHeight ? bitmap.Height - i : LineHeight;
                    var area = new Rectangle(0, i, bitmap.Width, lineHeight);
                    var croppedImage = bitmap.CropImage(area);
                    var recognizedLine = RecognizeLine(croppedImage);
                    if (string.IsNullOrEmpty(recognizedLine))
                        continue;
                    recognizedText.AppendLine(recognizedLine);
                }
                return recognizedText.ToString();
            }
        }

        private string RecognizeLine(Bitmap source)
        {
            var bmData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            var isEmptyPixelColumn = true;
            var countOfEmptyPixelColumns = 0;
            var stride = bmData.Stride;
            var xMax = 0;
            var yMax = 0;
            var xMin = source.Width;
            var yMin = source.Height;
            var recognizedLine = new StringBuilder();

            try
            {
                unsafe
                {
                    for (var x = 0; x < source.Width; ++x)
                    {
                        var p = (byte*)(void*)bmData.Scan0;
                        p += 4 * x;
                        for (var y = 0; y < source.Height; ++y)
                        {
                            if (p[0] == 0 && p[1] == 0 && p[2] == 0)
                            {
                                if (x > xMax)
                                    xMax = x;
                                if (x < xMin)
                                    xMin = x;
                                if (y > yMax)
                                    yMax = y;
                                if (y < yMin)
                                    yMin = y;
                                isEmptyPixelColumn = false;
                                countOfEmptyPixelColumns = 0;
                            }
                            p += stride;
                        }
                        if (isEmptyPixelColumn)
                        {
                            countOfEmptyPixelColumns++;
                            if (xMax != 0 && yMax != 0)
                            {
                                var recognizedChar = RecognizeCharacter(source, xMin, yMin, xMax, yMax);
                                recognizedLine.Append(recognizedChar);
                            }
                            if (recognizedLine.Length > 0)
                            {
                                if (countOfEmptyPixelColumns == 3 && recognizedLine[recognizedLine.Length - 1] != '1')
                                    recognizedLine.Append(" ");
                                if (countOfEmptyPixelColumns > 3 && recognizedLine[recognizedLine.Length - 1] != ' ')
                                    recognizedLine.Append(" ");
                            }
                            xMax = yMax = 0;
                            xMin = source.Width;
                            yMin = source.Height;
                        }
                        isEmptyPixelColumn = true;
                    }
                }
            }
            finally
            {
                source.UnlockBits(bmData);
            }
            return recognizedLine.ToString();
        }

        private string RecognizeCharacter(Bitmap source, int xStart, int yStart, int xEnd, int yEnd)
        {
            var start = new Point(xStart, yStart);
            var size = new Size(xEnd - xStart + 1, yEnd - yStart + 1);
            var section = new Rectangle(start, size);
            var charBitmap = source.CropImage(section);
            if (charBitmap.Width > 11) // there are probably two characters in charBitmap
            {
                return BruteForceSearchCharacters(charBitmap);
            }
            else
            {
                foreach (var charPattern in _charPatterns)
                    if (charBitmap.CompareMemCmp(charPattern.Bitmap))
                        return charPattern.Character;
            }
            return "*";
        }

        private string BruteForceSearchCharacters(Bitmap source)
        {
            var result = new StringBuilder();
            for (var x = 0; x < source.Width; x++)
            {
                foreach (var charPattern in _charPatterns.Where(p => !p.IsSpecialSymbol))
                {
                    if (IsBruteForceCharacterMatch(source, charPattern, x))
                    {
                        result.Append(charPattern.Character);
                        x += charPattern.Bitmap.Width - 1;
                        break;
                    }
                }
            }
            return result.ToString();
        }

        private static bool IsBruteForceCharacterMatch(Bitmap source, CharPattern charPattern, int x)
        {
            var charPatternSize = new Size(charPattern.Bitmap.Width, charPattern.Bitmap.Height);
            for (var y = 0; y < source.Height - charPattern.Bitmap.Height + 1; y++)
            {
                var start = new Point(x, y);
                var section = new Rectangle(start, charPatternSize);
                if (x + charPatternSize.Width > source.Width || y + charPatternSize.Height > source.Height)
                    return false;
                var charBitmap = source.CropImage(section);

                if (charBitmap.CompareMemCmp(charPattern.Bitmap))
                    return true;
            }
            return false;
        }
    }
}
